#include "server.h" 

#include <sys/types.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/stat.h>
#ifndef WIN32
#include <sys/queue.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/socket.h>
#else
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include <signal.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <event.h>
#include <evhttp.h>

namespace PESI {


struct RealServer{
  RealServer( const Router &router );
  ~RealServer();

  int start( const char *addr, int port );

  void request( struct evhttp_request *req );
private:
	struct evhttp *_http_server;
  const Router &_router;
};

// generic callback to RealServer::request
static void http_basic_cb(struct evhttp_request *req, void *server)
{
  ((RealServer*)server)->request(req);
}

RealServer::RealServer( const Router &router ) : _http_server(0), _router(router)
{
}
RealServer::~RealServer()
{
  if( _http_server ){ evhttp_free( _http_server ); }
}

int RealServer::start( const char *addr, int port )
{
	/* Initalize the event library */
	event_init();

  printf( "starting: %s:%d\n", addr, port);
  this->_http_server = evhttp_start( addr, (u_short)port );
  if( !this->_http_server ){
    fprintf( stderr, "Failed to startup server: !\n" );
    return 1;
  }
  evhttp_set_gencb( this->_http_server, http_basic_cb, this );
  printf( "listening...\n");
 
  while(1){
    printf( "waiting...\n");
    event_dispatch();
  }
  return 0;
}
void RealServer::request( struct evhttp_request *req )
{
	struct evbuffer *evb = evbuffer_new();

	// evbuffer_add_printf(evb, "How about those yankees?");
	// evhttp_send_reply(req, HTTP_OK, "", evb);

	evbuffer_free(evb);

  // TODO: start requesting from the backend
  // resolve the request to a specific backend server
  // start requesting from the backend server
  // as we're receiving parts from the backend server
  // start parsing those results using libxml chunk parser
  // also as we have enough information about the backend request
  // send the response header with Transfer-encoding: chunked
  //
  // as we parse the backend and find an esi:include, start buffering the 
  // data from the backend server as we make start the include request
  //
  // the includes will hopefully be nicely pipelined as below:
  //
  // R---E--E--S-F-S-C
  //     |  | /   /
  //     B---/   /
  //        |   /
  //        B__/
  //
  // R : Initial request
  // E : ESI Include in requested document
  // B : Backend Response
  // F : Finished requesting Primary document 
  // S : Store the response Fragment in our Cache
  // C : Completed response to Client
  //
  // In order for this to work we'll need a response buffer
  // up until the first E we'll continue to stream the response back to the client.
  //
  // Before that esi:include is finished we've continued parsing the contents streaming back from the initial request
  // and detected another esi:include, which we've started to request.
  //
  // The first esi:include is finished and we store it in our cache.
  //
  // Next the initial response, R has completed and we send as much back to the client as possible while waiting on the 
  // last esi:include response to return or timeout.
  //
  // The final esi fragment is returned and we combine it to the remaining response buffer as well as store it in the cache
  // The last bytes are sent to the Client finishing off the full HTTP Request/Response Cycle.
}


Server::Server(const Options &options)
{
  printf( "checking for config\n" );
  if( options.hasOpt("config") ){
    printf( "loading router\n" );
    this->_router.load(options["config"]);
  }
  this->_server = new RealServer(this->_router);
}
Server::~Server()
{
  delete this->_server;
}

int Server::start()
{
  const char *addr = _options["address"]; // "127.0.0.1";
  const char *port = _options["port"]; // "5000";
 
  if( !addr || strlen(addr) == 0 ){ addr = "127.0.0.1"; }
  if( !port || strlen(port) == 0 ){ port = "5000"; }

  printf( "starting up server on %s:%s\n", addr, port );
  return this->_server->start( addr, atoi(port) );
}


}// end namespace PESI
